package org.openmore.platform.wechatMp;

import lombok.AllArgsConstructor;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpMessageRouter;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.menu.WxMpMenu;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import me.chanjar.weixin.mp.config.impl.WxMpRedisConfigImpl;
import me.chanjar.weixin.mp.constant.WxMpEventConstants;
import org.apache.commons.lang3.StringUtils;
import org.openmore.common.exception.OpenmoreException;
import org.openmore.platform.dao.PlatformConfigMapper;
import org.openmore.platform.entity.PlatformConfig;
import org.openmore.platform.entity.enums.PlatformType;
import org.openmore.platform.service.BaseServiceImpl;
import org.openmore.platform.wechatMp.handler.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;
import redis.clients.jedis.JedisPool;
import tk.mybatis.mapper.entity.Example;

import java.util.HashMap;
import java.util.Map;

/**微信公众号(mp) 开发文档（包含网页授权、网站登录等相关接口）*/
@Service
public class WechatMpServiceImpl extends BaseServiceImpl implements WechatMpService {

    private static Map<String, WxMpService> sWechatServiceMap;
    //  微信服务处理器
    private Map<String, WxMpMessageRouter> wechatMsgRouterMap = new HashMap<>();

    @Autowired
    private PlatformConfigMapper platformConfigMapper;

    @Autowired
    private JedisPool jedisPool;

    @Autowired
    private LogHandler logHandler;
    @Autowired
    private NullHandler nullHandler;
    @Autowired
    private KfSessionHandler kfSessionHandler;
    @Autowired
    private StoreCheckNotifyHandler storeCheckNotifyHandler;
    @Autowired
    private LocationHandler locationHandler;
    @Autowired
    private MenuHandler menuHandler;
    @Autowired
    private MsgHandler msgHandler;
    @Autowired
    private UnsubscribeHandler unsubscribeHandler;
    @Autowired
    private SubscribeHandler subscribeHandler;


    /**
     * 单例获得所有的微信配置
     *
     * @return
     */
    public static Map<String, WxMpService> getInstance() {
        if (sWechatServiceMap == null) {
            sWechatServiceMap = new HashMap<>();
        }
        return sWechatServiceMap;
    }

    public WxMpMessageRouter getMpRouter(String tenantId){
        return wechatMsgRouterMap.get(PlatformType.WECHAT_MP+tenantId);
    }

    @Override
    public WxMpService getWxService(String tenantId) {
        if (org.springframework.util.StringUtils.isEmpty(tenantId)) {
            throw new OpenmoreException("租户id不能为空");
        }

        Map<String, WxMpService> serviceMap = getInstance();
        WxMpService wxService = serviceMap.get(tenantId);
        if (wxService == null) {
//          没有微信服务，则根据tenantid获得对应的微信配置
            Example example = new Example(PlatformConfig.class);
            Example.Criteria criteria = example.createCriteria().andEqualTo("platformType", PlatformType.WECHAT_MP).andEqualTo("deleted", false);
            if(!StringUtils.isEmpty(tenantId)){
                criteria.andEqualTo("tenantId", tenantId);
            }
            PlatformConfig wxConfig = platformConfigMapper.selectOneByExample(example);
            if (wxConfig == null) {
                throw new OpenmoreException("找不到租户的微信配置信息");
            }
            WxMpRedisConfigImpl configStorage = new WxMpRedisConfigImpl(jedisPool);
            configStorage.setAppId(wxConfig.getAppid());
            configStorage.setAesKey(wxConfig.getAesKey());
            configStorage.setSecret(wxConfig.getSecret());
            configStorage.setToken(wxConfig.getToken());
//          创建微信服务对象
            wxService = new WxMpServiceImpl();
            wxService.setWxMpConfigStorage(configStorage);
            // 缓存起来
            serviceMap.put(tenantId, wxService);
            wechatMsgRouterMap.put(PlatformType.WECHAT_MP+tenantId, newMsgRouter(tenantId));
        }
        return wxService;
    }


    /**
     * 获得指定租户id对应的微信服务器回调处理器，使用到了懒加载，只到执行前才添加处理器
     *
     * @param tenantId
     * @return
     */
    private WxMpMessageRouter newMsgRouter(String tenantId) {
        WxMpMessageRouter router = wechatMsgRouterMap.get(tenantId);
        if (router == null) {
            router = new WxMpMessageRouter(getWxService(tenantId));
            // 记录所有事件的日志 （异步执行）
            router.rule().handler(this.logHandler).next();

            // 接收客服会话管理事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxMpEventConstants.CustomerService.KF_CREATE_SESSION)
                    .handler(this.kfSessionHandler).end();
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxMpEventConstants.CustomerService.KF_CLOSE_SESSION)
                    .handler(this.kfSessionHandler)
                    .end();
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxMpEventConstants.CustomerService.KF_SWITCH_SESSION)
                    .handler(this.kfSessionHandler).end();

            // 门店审核事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxMpEventConstants.POI_CHECK_NOTIFY)
                    .handler(this.storeCheckNotifyHandler).end();

            // 自定义菜单事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxConsts.MenuButtonType.CLICK).handler(this.menuHandler).end();

            // 点击菜单连接事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxConsts.MenuButtonType.VIEW).handler(this.nullHandler).end();

            // 关注事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxConsts.EventType.SUBSCRIBE).handler(this.subscribeHandler)
                    .end();

            // 取消关注事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxConsts.EventType.UNSUBSCRIBE)
                    .handler(this.unsubscribeHandler).end();

            // 上报地理位置事件
            router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
                    .event(WxConsts.EventType.LOCATION).handler(this.locationHandler)
                    .end();

            // 接收地理位置消息
            router.rule().async(false).msgType(WxConsts.XmlMsgType.LOCATION)
                    .handler(this.locationHandler).end();

            // 扫码事件
            //        router.rule().async(false).msgType(WxConsts.XmlMsgType.EVENT)
            //                .event(WxConsts.EventType.SCAN).handler(this.getScanHandler()).end();

            // 默认
            router.rule().async(false).handler(this.msgHandler).end();
            // 缓存起来
            wechatMsgRouterMap.put(PlatformType.WECHAT_MP+tenantId, router);
        }
        return router;
    }

    @Override
    public String checkSignature(String tenantId, String signature, String timestamp, String nonce, String echostr) {
        logger.info("\n接收到来自微信服务器的认证消息：[{}, {}, {}, {}]", signature,
                timestamp, nonce, echostr);
        if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
            throw new IllegalArgumentException("请求参数非法，请核实!");
        }

        if (getWxService(tenantId).checkSignature(timestamp, nonce, signature)) {
            return echostr;
        }

        return "非法请求";
    }

    @Override
    public String handlePost(String tenantId, String requestBody, String signature, String timestamp, String nonce, String openid, String encType, String msgSignature) {
        logger.info("\n接收微信请求：[openid=[{}], [signature=[{}], encType=[{}], msgSignature=[{}],"
                        + " timestamp=[{}], nonce=[{}], requestBody=[\n{}\n] ",
                openid, signature, encType, msgSignature, timestamp, nonce, requestBody);

        if (!getWxService(tenantId).checkSignature(timestamp, nonce, signature)) {
            throw new IllegalArgumentException("非法请求，可能属于伪造的请求！");
        }

        String out = null;
        if (encType == null) {
            // 明文传输的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromXml(requestBody);
            WxMpXmlOutMessage outMessage = getMpRouter(tenantId).route(inMessage);
            if (outMessage == null) {
                return "";
            }

            out = outMessage.toXml();
        } else if ("aes".equalsIgnoreCase(encType)) {
            // aes加密的消息
            WxMpXmlMessage inMessage = WxMpXmlMessage.fromEncryptedXml(requestBody, getWxService(tenantId).getWxMpConfigStorage(),
                    timestamp, nonce, msgSignature);
            logger.debug("\n消息解密后内容为：\n{} ", inMessage.toString());
            WxMpXmlOutMessage outMessage = getMpRouter(tenantId).route(inMessage);
            if (outMessage == null) {
                return "";
            }

            out = outMessage.toEncryptedXml(getWxService(tenantId).getWxMpConfigStorage());
        }

        logger.debug("\n组装回复信息：{}", out);
        return out;
    }

    private WxMpXmlOutMessage route(String tenantId, WxMpXmlMessage message) {
        try {
            return getMpRouter(tenantId).route(message);
        } catch (Exception e) {
            logger.error("路由消息时出现异常！", e);
        }

        return null;
    }

    /**网页授权获取授权身份信息*/
    @Override
    public WxMpUser greetUser(String tenantId, String code){
        WxMpUser wxMpUser = null;
        try {
            WxMpOAuth2AccessToken accessToken = getWxService(tenantId).oauth2getAccessToken(code);
            wxMpUser = getWxService(tenantId).oauth2getUserInfo(accessToken, null);
        } catch (WxErrorException e) {
            e.printStackTrace();
        }
        return wxMpUser;
    }

    /**创建按钮*/
    @Override
    public String menuCreate(String tenantId, WxMenu menu){
        try {
            return getWxService(tenantId).getMenuService().menuCreate(menu);
        }catch (WxErrorException wxException){
            throw new OpenmoreException(400, "[code:"+wxException.getError().getErrorCode()+"]:"+wxException.getError().getErrorMsg());
            //return this.wxService.getMenuService().menuCreate(menu);
        }
    }

    /**自定义菜单创建*/
    @Override
    public String menuCreate(String tenantId, String json){
        try {
            return getWxService(tenantId).getMenuService().menuCreate(json);
        }catch (WxErrorException wxException){
            throw new OpenmoreException(400, "[code:"+wxException.getError().getErrorCode()+"]:"+wxException.getError().getErrorMsg());
            //return this.wxService.getMenuService().menuCreate(menu);
        }
    }
    /**自定义菜单删除*/
    @Override
    public void menuDelete(String tenantId){
        try {
            getWxService(tenantId).getMenuService().menuDelete();
        }catch (WxErrorException wxException){
            throw new OpenmoreException(400, "[code:"+wxException.getError().getErrorCode()+"]:"+wxException.getError().getErrorMsg());
            //return this.wxService.getMenuService().menuCreate(menu);
        }
    }
    /**删除个性化菜单按钮*/
    @Override
    public void menuDelete(String tenantId, String menuId) {
        try {
            getWxService(tenantId).getMenuService().menuDelete(menuId);
        }catch (WxErrorException wxException){
            throw new OpenmoreException(400, "[code:"+wxException.getError().getErrorCode()+"]:"+wxException.getError().getErrorMsg());
            //return this.wxService.getMenuService().menuCreate(menu);
        }
    }

    /**自定义菜单查询接口*/
    @Override
    public WxMpMenu menuGet(String tenantId) {
        try {
            return getWxService(tenantId).getMenuService().menuGet();
        }catch (WxErrorException wxException){
            throw new OpenmoreException(400, "[code:"+wxException.getError().getErrorCode()+"]:"+wxException.getError().getErrorMsg());
            //return this.wxService.getMenuService().menuCreate(menu);
        }
    }

}
